import service
import blue
import util
import os
import audio2
import trinity
import log
import collections
import geo2
import uthread
import GameWorld
import audio

def Audio2JukeboxCallback(*args):
    for arg in args:
        sm.StartService('audio').OnWiseJukeboxEvent(arg)




class AudioService(service.Service):
    __guid__ = 'svc.audio'
    __sessionparams__ = []
    __exportedcalls__ = {'IsAudioSupportedBySystem': [],
     'Activate': [],
     'Deactivate': [],
     'GetMasterVolume': [],
     'SetMasterVolume': [],
     'GetUIVolume': [],
     'SetUIVolume': [],
     'GetWorldVolume': [],
     'SetWorldVolume': [],
     'SetMusicVolume': [],
     'IsActivated': []}
    __dependencies__ = []
    __notifyevents__ = []
    __componentTypes__ = ['audioEmitter']

    def __init__(self):
        service.Service.__init__(self)
        self.AppInit()



    def Run(self, ms = None):
        self.active = False
        self.manager = audio2.GetManager()
        aPath = blue.os.ResolvePath(u'res:/Audio')
        io = audio2.AudLowLevelIO(aPath)
        initConf = audio2.AudConfig()
        initConf.lowLevelIO = io
        initConf.numRefillsInVoice = 8
        initConf.asyncFileOpen = True
        self.manager.config = initConf
        self.banksLoaded = False
        enabled = self.AppGetSetting('audioEnabled', 1)
        self.uiPlayer = self.jukeboxPlayer = None
        self.audioEmitterComponentsByScene = collections.defaultdict(dict)
        self.AppRun()
        if enabled:
            self.Activate()



    def Stop(self, stream):
        self.uiPlayer = None
        self.jukeboxPlayer = None
        self.AppStop()



    def SetGlobalRTPC(self, rtpcName, value):
        if not self.IsActivated():
            return 
        audio2.SetGlobalRTPC(unicode(rtpcName), value)



    def OnWiseJukeboxEvent(self, eventName):
        sm.ScatterEvent('OnWiseJukeboxEventReceived', eventName)



    def IsAudioSupportedBySystem(self):
        return audio2.HasSSESupport()



    def Activate(self):
        if audio2.HasSSESupport():
            self.manager.SetEnabled(True)
            if (self.uiPlayer is None):
                self.uiPlayer = audio2.GetUIPlayer()
            if (self.jukeboxPlayer is None):
                self.jukeboxPlayer = audio2.GetJukebox()
                jukeboxEventHandler = blue.os.CreateInstance('blue.BlueEventToPython')
                jukeboxEventHandler.handler = Audio2JukeboxCallback
                self.jukeboxPlayer.eventListener = jukeboxEventHandler
        else:
            self.AppMessage('SystemDoesNotSupportAudio')
            self.AppSetSetting('audioEnabled', 0)
            return 
        if not self.banksLoaded:
            self.manager.LoadBank(u'Init.bnk')
            self.AppLoadBanks()
            self.banksLoaded = True
        self.active = True
        self.AppActivate()
        try:
            self.AppSetListener(audio2.GetListener(0))
        except:
            pass
        sm.ScatterEvent('OnAudioActivated')



    def Deactivate(self):
        if audio2.HasSSESupport():
            self.manager.SetEnabled(False)
        self.AppDeactivate()
        self.active = False
        sm.ScatterEvent('OnAudioDeactivated')



    def SetMasterVolume(self, vol = 1.0, persist = True):
        if ((vol < 0.0) or (vol > 1.0)):
            raise RuntimeError('Erroneous value received for volume')
        self.SetGlobalRTPC(unicode('volume_master'), vol)
        if persist:
            self.AppSetSetting('masterVolume', vol)



    def GetMasterVolume(self):
        return self.AppGetSetting('masterVolume', 0.4)



    def SetUIVolume(self, vol = 1.0, persist = True):
        if ((vol < 0.0) or (vol > 1.0)):
            raise RuntimeError('Erroneous value received for volume')
        self.SetGlobalRTPC(unicode('volume_ui'), vol)
        if persist:
            self.AppSetSetting('uiGain', vol)



    def GetUIVolume(self):
        return self.AppGetSetting('uiGain', 0.4)



    def SetWorldVolume(self, vol = 1.0, persist = True):
        if ((vol < 0.0) or (vol > 1.0)):
            raise RuntimeError('Erroneous value received for volume')
        self.SetGlobalRTPC(unicode('volume_world'), vol)
        if persist:
            self.AppSetSetting('worldVolume', vol)



    def GetWorldVolume(self):
        return self.AppGetSetting('worldVolume', 0.4)



    def SetMusicVolume(self, volume = 1.0, persist = True):
        volume = min(1.0, max(0.0, volume))
        self.SetGlobalRTPC(unicode('volume_music'), volume)
        if persist:
            self.AppSetSetting('eveampGain', volume)



    def IsActivated(self):
        return self.active



    def SendUIEvent(self, event):
        if not self.IsActivated():
            self.LogInfo('Audio inactive - skipping UI event', event)
            return 
        if event.startswith('wise:/'):
            event = event[6:]
        self.LogInfo('Sending UI event to WWise:', event)
        self.uiPlayer.SendEvent(unicode(event))



    def SendJukeboxEvent(self, event):
        if not self.IsActivated():
            self.LogWarn('Audio inactive - skipping jukebox event', event)
            return 
        if event.startswith('wise:/'):
            event = event[6:]
        self.LogInfo('Sending Jukebox event to WWise:', event)
        self.jukeboxPlayer.SendEvent(unicode(event))



    def PlayJukeboxTrack(self, track):
        if not self.IsActivated():
            self.LogWarn('Audio inactive - skipping play track request', track)
            return 
        self.LogInfo('Playing Jukebox track', track)
        self.jukeboxPlayer.Play(unicode(track))



    def AppInit(self):
        return 



    def AppRun(self):
        return 



    def AppStop(self):
        return 



    def AppLoadBanks(self):
        return 



    def AppSetListener(self, listener):
        return 



    def AppActivate(self):
        return 



    def AppDeactivate(self):
        return 



    def AppMessage(self, message):
        return 



    def AppGetSetting(self, setting, default):
        try:
            return settings.public.audio.Get(setting, default)
        except (AttributeError, NameError):
            return default



    def AppSetSetting(self, setting, value):
        try:
            settings.public.audio.Set(setting, value)
        except (AttributeError, NameError):
            pass



    def CreateComponent(self, name, state):
        component = audio.AudioEmitterComponent()
        component.initialEventName = state.get(audio.INITIAL_EVENT_NAME, None)
        component.initialSoundID = state.get(audio.INITIAL_SOUND_ID, None)
        component.emitter = None
        return component



    def PackUpForSceneTransfer(self, component, destinationSceneID):
        state = {}
        if self.initialEventName:
            state[audio.INITIAL_EVENT_NAME] = self.initialEventName
        if self.initialSoundID:
            state[audio.INITIAL_SOUND_ID] = self.initialSoundID
        return True



    def UnPackFromSceneTransfer(self, component, entity, state):
        component.initialEventName = state.get(audio.INITIAL_EVENT_NAME, None)
        component.initialSoundID = state.get(audio.INITIAL_SOUND_ID, None)
        return component



    def SetupComponent(self, entity, component):
        self.audioEmitterComponentsByScene[entity.scene.sceneID][entity.entityID] = component
        component.emitter = audio2.AudEmitter(('AudEmitter_' + str(entity.entityID)))
        positionComponent = entity.GetComponent('position')
        positionComponent.RegisterPlacementObserverWrapper(GameWorld.PlacementObserverWrapper(component.emitter))
        if component.initialEventName:
            component.emitter.SendEvent(unicode(component.initialEventName))
        if component.initialSoundID:
            sound = cfg.sounds.GetIfExists(component.initialSoundID)
            if sound:
                component.emitter.SendEvent(unicode(sound.soundFile[6:]))



    def UnRegisterComponent(self, entity, component):
        del self.audioEmitterComponentsByScene[entity.scene.sceneID][entity.entityID]
        component.emitter.SendEvent(u'fade_out')
        component.emitter = None



    def OnEntitySceneLoaded(self, sceneID):
        uthread.new(self.SetupPlayerListener)



    def SetupPlayerListener(self):
        player = self.entityService.GetPlayerEntity(canBlock=True)
        if (player and player.HasComponent('position')):
            listener = audio2.GetListener(0)
            positionComponent = player.GetComponent('position')
            self.playerListener = GameWorld.PlacementObserverWrapper(listener)
            positionComponent.RegisterPlacementObserverWrapper(self.playerListener)



    def OnEntitySceneUnloaded(self, sceneID):
        for component in self.audioEmitterComponentsByScene[sceneID]:
            component.emitter.SendEvent(u'fade_out')
            component.emitter = None

        if (sceneID in self.audioEmitterComponentsByScene):
            del self.audioEmitterComponentsByScene[sceneID]
        player = self.entityService.GetPlayerEntity()
        if (player and player.HasComponent('position')):
            positionComponent = player.GetComponent('position')
            positionComponent.UnRegisterPlacementObserverWrapper(self.playerListener)
            self.playerListener = None



    def ReportState(self, component):
        state = collections.OrderedDict()
        state['Initial Sound ID'] = component.initialSoundID
        state['Initial Event Name'] = component.initialEventName
        return state




